// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.ui.prefs.connectivity;

import java.util.List;

import org.dengues.commons.connectivity.IConnectivitySettingsConstants;
import org.dengues.core.i18n.Messages;
import org.dengues.ui.prefs.connectivity.models.ConnectivityCategory;
import org.dengues.ui.prefs.connectivity.models.ConnectivityGenCategory;
import org.dengues.ui.prefs.connectivity.models.ConnectivityProperty;
import org.dengues.ui.prefs.connectivity.models.ConnectivityProvider;
import org.dengues.ui.prefs.connectivity.models.ConnectivitySubCategory;
import org.dengues.ui.prefs.connectivity.ui.ConnectivityDialog;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;

/**
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf 2007-11-23 qiang.zhang $
 * 
 */
public class ConnectivitySettingsPrefsPage extends PreferencePage implements IWorkbenchPreferencePage {

    // ui elements
    private Button addButton;

    private Button removeButton;

    private Button editButton;

    private Button copyButton;

    private Label errorLabel;

    private TreeViewer treeViewer;

    private Action addAction;

    private Action removeAction;

    private Action editAction;

    private Action copyAction;

    // dirty flag for save/no save
    private boolean dirty = false;

    private ConnectivitySettingTreeProvider settingTreeProvider;

    private String errorMessage;

    /**
     * Qiang.Zhang.Adolf@gmail.com ConnectivitySettingsPrefsPage constructor comment.
     */
    public ConnectivitySettingsPrefsPage() {
        super();
    }

    /**
     * Constructor with a page title
     * 
     * @param title
     */
    public ConnectivitySettingsPrefsPage(String title) {
        super(title);
    }

    /**
     * Constructor with a title and icon
     * 
     * @param title
     * @param image
     */
    public ConnectivitySettingsPrefsPage(String title, ImageDescriptor image) {
        super(title, image);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
     */
    @Override
    protected Control createContents(Composite parent) {
        Font font = parent.getFont();

        Composite content = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        layout.numColumns = 2;
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        content.setLayout(layout);
        GridData data = new GridData();
        data.verticalAlignment = GridData.FILL;
        data.horizontalAlignment = GridData.FILL;
        content.setLayoutData(data);
        content.setFont(font);

        Label label = new Label(content, SWT.LEFT);
        label.setText(Messages.getString("ConnectivitySettingsPrefsPage.title")); //$NON-NLS-1$
        data = new GridData();
        data.horizontalAlignment = GridData.FILL;
        data.horizontalSpan = 2;
        label.setLayoutData(data);
        label.setFont(font);

        this.errorLabel = new Label(content, SWT.LEFT | SWT.WRAP);
        data = new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL);
        data.horizontalSpan = 2;
        data.heightHint = 35;
        this.errorLabel.setLayoutData(data);
        this.errorLabel.setForeground(JFaceColors.getErrorText(getControl().getDisplay()));
        this.errorLabel.setFont(font);

        this.treeViewer = new TreeViewer(content, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
        data = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);
        data.verticalSpan = 10;
        this.treeViewer.getTree().setLayoutData(data);
        this.treeViewer.getTree().setFont(font);

        makeActions();
        hookContextMenu();
        settingTreeProvider = new ConnectivitySettingTreeProvider();
        this.treeViewer.setContentProvider(settingTreeProvider);
        this.treeViewer.setLabelProvider(settingTreeProvider);
        this.treeViewer.setSorter(new ViewerSorter());
        this.treeViewer.setInput(ConnectivityProvider.getInstance());
        this.treeViewer.expandToLevel(3);

        this.treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {

            public void selectionChanged(SelectionChangedEvent event) {
                StructuredSelection selection = (StructuredSelection) ConnectivitySettingsPrefsPage.this.treeViewer
                        .getSelection();

                if (selection != null && selection.size() > 0 && selection.getFirstElement() != null) {
                    updateButtons(selection.getFirstElement());
                } else {
                    updateButtons(null);
                }
            }

        });

        this.treeViewer.addDoubleClickListener(new IDoubleClickListener() {

            public void doubleClick(DoubleClickEvent event) {
                StructuredSelection selection = (StructuredSelection) ConnectivitySettingsPrefsPage.this.treeViewer
                        .getSelection();
                editDriver(selection);
            }
        });
        Composite groupComponent = new Composite(content, SWT.NULL);
        GridLayout groupLayout = new GridLayout();
        groupLayout.marginWidth = 0;
        groupLayout.marginHeight = 0;
        groupComponent.setLayout(groupLayout);
        data = new GridData();
        data.verticalAlignment = GridData.FILL;
        data.horizontalAlignment = GridData.FILL;
        groupComponent.setLayoutData(data);
        groupComponent.setFont(font);

        this.addButton = new Button(groupComponent, SWT.PUSH);
        this.addButton.setText(Messages.getString("ConnectivitySettingsPrefsPage.add")); //$NON-NLS-1$
        this.addButton.setLayoutData(data);
        this.addButton.setFont(font);
        setButtonLayoutData(this.addButton);
        this.addButton.addSelectionListener(new SelectionListener() {

            public void widgetSelected(SelectionEvent e) {
                addDriver(ConnectivitySettingsPrefsPage.this.treeViewer.getSelection());
            }

            public void widgetDefaultSelected(SelectionEvent e) {
                widgetSelected(e);
            }

        });

        this.editButton = new Button(content, SWT.PUSH);
        this.editButton.setText(Messages.getString("ConnectivitySettingsPrefsPage.edit")); //$NON-NLS-1$
        this.editButton.setLayoutData(data);
        this.editButton.setFont(font);
        setButtonLayoutData(this.editButton);
        this.editButton.addSelectionListener(new SelectionListener() {

            public void widgetSelected(SelectionEvent e) {
                editDriver(ConnectivitySettingsPrefsPage.this.treeViewer.getSelection());
            }

            public void widgetDefaultSelected(SelectionEvent e) {
                widgetSelected(e);
            }

        });

        this.removeButton = new Button(content, SWT.PUSH);
        this.removeButton.setText(Messages.getString("ConnectivitySettingsPrefsPage.remove")); //$NON-NLS-1$
        this.removeButton.setLayoutData(data);
        this.removeButton.setFont(font);
        setButtonLayoutData(this.removeButton);
        this.removeButton.addSelectionListener(new SelectionListener() {

            public void widgetSelected(SelectionEvent e) {
                removeDriver(ConnectivitySettingsPrefsPage.this.treeViewer.getSelection());
            }

            public void widgetDefaultSelected(SelectionEvent e) {
                widgetSelected(e);
            }

        });

        this.copyButton = new Button(content, SWT.PUSH);
        this.copyButton.setText(Messages.getString("ConnectivitySettingsPrefsPage.copy")); //$NON-NLS-1$
        this.copyButton.setLayoutData(data);
        this.copyButton.setFont(font);
        setButtonLayoutData(this.copyButton);
        this.copyButton.addSelectionListener(new SelectionListener() {

            public void widgetSelected(SelectionEvent e) {
                copyDriver(ConnectivitySettingsPrefsPage.this.treeViewer.getSelection());
            }

            public void widgetDefaultSelected(SelectionEvent e) {
                widgetSelected(e);
            }

        });

        updateButtons(null);
        return content;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "updateButtons".
     * 
     * @param selection
     */
    private void updateButtons(Object selection) {
        // if they selected a category...
        if (selection instanceof ConnectivitySubCategory) {
            addButton.setEnabled(true);
            addAction.setEnabled(true);
            removeButton.setEnabled(false);
            removeAction.setEnabled(false);
            editButton.setEnabled(false);
            editAction.setEnabled(false);
            copyButton.setEnabled(false);
            copyAction.setEnabled(false);
            errorLabel.setText(""); //$NON-NLS-1$
            errorLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
        } else if (selection instanceof ConnectivityGenCategory) {
            boolean b = ((ConnectivityGenCategory) selection).getType().getUrl() != null;
            addButton.setEnabled(b);
            addAction.setEnabled(b);
            removeButton.setEnabled(false);
            removeAction.setEnabled(false);
            editButton.setEnabled(false);
            editAction.setEnabled(false);
            copyButton.setEnabled(false);
            copyAction.setEnabled(false);
            errorLabel.setText(""); //$NON-NLS-1$
            errorLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
        }
        // // if they selected a driver instance
        else if (selection instanceof ConnectivityProperty) {
            addButton.setEnabled(false);
            addAction.setEnabled(false);
            removeButton.setEnabled(true);
            removeAction.setEnabled(true);
            editButton.setEnabled(true);
            editAction.setEnabled(true);
            copyButton.setEnabled(true);
            copyAction.setEnabled(true);

            ConnectivityProperty ps = (ConnectivityProperty) selection;
            if (ps != null) {
                validate(ps);
            }
        } else {
            addButton.setEnabled(false);
            addAction.setEnabled(false);
            removeButton.setEnabled(false);
            removeAction.setEnabled(false);
            editButton.setEnabled(false);
            editAction.setEnabled(false);
            copyButton.setEnabled(false);
            copyAction.setEnabled(false);
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "addDriver".
     * 
     * @param selection
     */
    private void addDriver(ISelection selection) {
        StructuredSelection sselection = (StructuredSelection) selection;

        // Add a new driver instance from one of the templates for the category
        if (sselection.getFirstElement() instanceof ConnectivityCategory) {

            ConnectivityCategory descriptor = (ConnectivityCategory) sselection.getFirstElement();
            List<String> existNames = settingTreeProvider.getExistPropertyNames(descriptor);
            ConnectivityDialog dlg = new ConnectivityDialog(getShell(), descriptor, existNames);
            if (dlg.open() == Window.OK) {

                ConnectivityProperty instance = dlg.getNewInstance();
                //
                dirty = true;
                // // stash the new instance
                settingTreeProvider.addConnectivityInstance(instance);

                saveChanges();
                // refresh
                instance.validate();
                treeViewer.refresh(descriptor);
                treeViewer.setExpandedState(descriptor, true);
            }
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "editDriver".
     * 
     * @param selection
     */
    private void editDriver(ISelection selection) {
        StructuredSelection sselection = (StructuredSelection) selection;
        if (sselection.getFirstElement() instanceof ConnectivityProperty) {
            ConnectivityProperty instance = (ConnectivityProperty) sselection.getFirstElement();
            List<String> existNames = settingTreeProvider.getExistPropertyNames(instance.getParent());
            String name = instance.getName();
            existNames.remove(name);
            ConnectivityDialog dlg = new ConnectivityDialog(getShell(), instance, existNames);
            if (dlg.open() == Window.OK) {
                dirty = true;
                settingTreeProvider.modifyConnectivityInstance(instance, name);
                // settingTreeProvider.addConnectivityInstance(instance);
                saveChanges();
                instance.validate();
                treeViewer.refresh(instance.getParent());
            }
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "removeDriver".
     * 
     * @param selection
     */
    private void removeDriver(ISelection selection) {
        StructuredSelection sselection = (StructuredSelection) selection;
        if (sselection.getFirstElement() instanceof ConnectivityProperty) {
            ConnectivityProperty instance = (ConnectivityProperty) sselection.getFirstElement();
            if (MessageDialog
                    .openQuestion(
                            getShell(),
                            Messages.getString("ConnectivitySettingsPrefsPage.delete.Title"), Messages.getString("ConnectivitySettingsPrefsPage.delete.msg", instance.getName() //$NON-NLS-1$ //$NON-NLS-2$
                                    )) == true) {
                settingTreeProvider.removeConnectivityInstance(instance);
                dirty = true;
                saveChanges();
                ConnectivityCategory parent = instance.getParent();
                treeViewer.refresh(parent);
                errorLabel.setText(""); //$NON-NLS-1$
            }
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "copyDriver".
     * 
     * @param selection
     */
    private void copyDriver(ISelection selection) {
        StructuredSelection sselection = (StructuredSelection) selection;
        if (sselection.getFirstElement() instanceof ConnectivityProperty) {
            ConnectivityProperty instance = (ConnectivityProperty) sselection.getFirstElement();
            if (instance != null) {
                ConnectivityProperty cloned = instance.clone();
                String copyPrefix = "_copy"; //$NON-NLS-1$
                String name = instance.getName() + copyPrefix;
                cloned.setName(name);

                settingTreeProvider.addConnectivityInstance(cloned);
                dirty = true;
                saveChanges();
                treeViewer.refresh(instance.getParent());
                treeViewer.setSelection(new StructuredSelection(cloned));
            }
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "makeActions".
     */
    private void makeActions() {
        this.addAction = new AddAction();
        this.addAction.setText(Messages.getString("ConnectivitySettingsPrefsPage.add")); //$NON-NLS-1$
        this.editAction = new EditAction();
        this.editAction.setText(Messages.getString("ConnectivitySettingsPrefsPage.edit")); //$NON-NLS-1$
        this.removeAction = new RemoveAction();
        this.removeAction.setText(Messages.getString("ConnectivitySettingsPrefsPage.remove")); //$NON-NLS-1$
        this.copyAction = new CopyAction();
        this.copyAction.setText(Messages.getString("ConnectivitySettingsPrefsPage.copy")); //$NON-NLS-1$
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "hookContextMenu".
     */
    private void hookContextMenu() {
        MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener() {

            public void menuAboutToShow(IMenuManager manager) {
                manager.add(ConnectivitySettingsPrefsPage.this.addAction);
                manager.add(ConnectivitySettingsPrefsPage.this.editAction);
                manager.add(ConnectivitySettingsPrefsPage.this.removeAction);
                manager.add(ConnectivitySettingsPrefsPage.this.copyAction);
            }
        });
        Menu menu = menuMgr.createContextMenu(this.treeViewer.getControl());
        treeViewer.getControl().setMenu(menu);
    }

    /**
     * Validate the property set and put an appropriate error message up
     * 
     * @param instance
     */
    private void validate(ConnectivityProperty instance) {
        if (!instance.isValid()) {
            errorMessage = instance.getStatusMessage();
        } else {
            errorMessage = ""; //$NON-NLS-1$
        }
        errorLabel.setText(errorMessage);
        errorLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
     */
    public void init(IWorkbench workbench) {
        // empty
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.preference.PreferencePage#performApply()
     */
    @Override
    protected void performApply() {
        // if there have been changes, save them
        if (this.dirty)
            saveChanges();
        super.performApply();
    }

    /**
     * Save the changes back to the instances file.
     */
    private void saveChanges() {
        // this code isn't needed any more because
        XMLFileManager.setFileName(IConnectivitySettingsConstants.CONNECTIVITY_FILE);
        List<ConnectivityProperty> psetsList = settingTreeProvider.getConnectivityInstances();
        Object[] objs = psetsList.toArray(new ConnectivityProperty[0]);
        ConnectivityProperty[] propsets = new ConnectivityProperty[objs.length];
        System.arraycopy(objs, 0, propsets, 0, objs.length);
        try {
            XMLFileManager.saveNamedPropertySet(propsets);
        } catch (CoreException e) {
            e.printStackTrace();
        }
        dirty = false;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.preference.IPreferencePage#performOk()
     */
    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.preference.PreferencePage#performOk()
     */
    @Override
    public boolean performOk() {
        // if there have been changes, save them
        if (this.dirty)
            saveChanges();
        return super.performOk();
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com ConnectivitySettingsPrefsPage class global comment. Detailled comment <br/>
     * 
     * $Id: Dengues.epf 2007-11-23 qiang.zhang $
     * 
     */
    private class AddAction extends Action {

        @Override
        public void run() {
            addDriver(treeViewer.getSelection());
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com ConnectivitySettingsPrefsPage class global comment. Detailled comment <br/>
     * 
     * $Id: Dengues.epf 2007-11-23 qiang.zhang $
     * 
     */
    private class RemoveAction extends Action {

        @Override
        public void run() {
            removeDriver(ConnectivitySettingsPrefsPage.this.treeViewer.getSelection());
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com ConnectivitySettingsPrefsPage class global comment. Detailled comment <br/>
     * 
     * $Id: Dengues.epf 2007-11-23 qiang.zhang $
     * 
     */
    private class EditAction extends Action {

        @Override
        public void run() {
            editDriver(ConnectivitySettingsPrefsPage.this.treeViewer.getSelection());
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com ConnectivitySettingsPrefsPage class global comment. Detailled comment <br/>
     * 
     * $Id: Dengues.epf 2007-11-23 qiang.zhang $
     * 
     */
    private class CopyAction extends Action {

        @Override
        public void run() {
            copyDriver(ConnectivitySettingsPrefsPage.this.treeViewer.getSelection());
        }
    }

}
